﻿package editor
{	
	import flash.display.MovieClip;
	import flash.display.Sprite;	
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.MouseEvent;
	import flash.geom.Point;

	public class Track extends MovieClip
	{
		public const KEYS:Array = new Array('SPACE' , 'A', 'S', 'D', 'F');
		
		protected static const LEFT:uint  = 37;
		protected static const RIGHT:uint = 39;
		protected static const UP:uint    = 38;
		protected static const DOWN:uint  = 40;		
		protected static const A:uint 	  = 65;
		protected static const S:uint     = 83;
		protected static const D:uint     = 68;
		protected static const F:uint     = 70;		
		protected static const SPACE:uint = 32;
		
		public const TRESHHOLD:int = 300;
		public const MARK_HEIGHT_ACTUAL: int = 7;
		
		private const MARK_HEIGHT: int = 60;		
		private const TRACK_WIDTH: int = 26;
		private const WIDTH: int = 130;
		
		private var FTracks:Vector.<MovieClip>;
		private var FMarks:Vector.<Sprite>;
		private var FTime:Number;
		private var FRatio:Number;
		private var FHeight:Number;
		private var FMarkHeight:Number;
		private var FCellsCount:int;
		
		public function Track():void
		{			
			FTracks = new Vector.<MovieClip>;
			FMarks = new Vector.<Sprite>;
			for(var i:int = 0; i<5; i++)
			{
				var vTrack:MovieClip = new MovieClip();				
				FTracks.push(vTrack);
			}
			FTime = new Number();
			FRatio = new Number();
			FHeight = new Number();
			FMarkHeight = new Number(MARK_HEIGHT);
			FCellsCount = 0;
			
			focusRect = false;
			
			addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown);
			addEventListener(Event.ENTER_FRAME, OnEnterFrame);
		}
		
		private function OnEnterFrame(e:Event):void 
		{
			if (stage)
			{
				if (stage.focus != this)
					stage.focus = this;
			}
		}
		
		private function OnKeyDown(e:KeyboardEvent):void 
		{
			var vKey:int = -1;
			switch(e.keyCode)
			{
				case LEFT:
				case A:
				{
					vKey = 1;
					break;					
				}		
				case DOWN:
				case S:				
				{
					vKey = 2;
					break;
				}
				case RIGHT:
				case D:
				{
					vKey = 3;
					break;
				}
				case UP:
				case F:
				{
					vKey = 4;
					break;
				}				
				case SPACE:
				{
					vKey = 0;
					break;
				}
			}
			if(vKey > -1)
				AddShapeAtY(vKey, -localToGlobal(new Point(x, y)).y + TRESHHOLD);
		}
		
		public function set Time(AValue:Number):void
		{
			FTime = AValue;
		}
		
		public function set Ratio(AValue:Number):void
		{
			FRatio = AValue;
		}
		
		public function get Ratio():Number
		{
			return FRatio;
		}
	
		public function Generate():void
		{
			for(var j in FTracks)
			{
				if(FTracks[j].parent)
					removeChild(FTracks[j]);
			}
			for(var k in FMarks)
			{
				if(FMarks[k].parent)
					removeChild(FMarks[k]);
			}
				
			var i:int;			
			FTracks = new Vector.<MovieClip>;
			FMarks = new Vector.<Sprite>;
			for(i = 0; i<5; i++)
				FTracks.push(new MovieClip());			
			FHeight = new Number();			
			FCellsCount = 0;
			
			var vHeight:Number = new Number(FTime * FRatio);
			FHeight = vHeight;
			//Body
			graphics.clear();
			graphics.beginFill(0xFF0000, 0);
			graphics.drawRect(0, 0, WIDTH, vHeight);			
			
			graphics.beginFill(0x333333, 1);
			graphics.drawRect(0, vHeight, WIDTH, TRESHHOLD);
									
			//Horizontal lines
			FCellsCount = Math.floor(vHeight / FMarkHeight);			
			graphics.lineStyle(1, 0x333333, 1);
			for(i=0; i < FCellsCount + 1; i++)
			{
				graphics.moveTo(0,     i * FMarkHeight); 
				graphics.lineTo(WIDTH, i * FMarkHeight);
			}
			graphics.endFill();
			//5 Tracks
			for(i = 0; i < FTracks.length; i++)
			{
				FTracks[i].graphics.beginFill(0xFF0000, 0);
				FTracks[i].graphics.drawRect(0, 0, TRACK_WIDTH, vHeight + TRESHHOLD);				
				FTracks[i].graphics.endFill();
				FTracks[i].x = TRACK_WIDTH * i;
				FTracks[i].ID = i;
				FTracks[i].addEventListener(MouseEvent.MOUSE_DOWN, OnTrackClick);
				addChild(FTracks[i]);
			}
		}
		
		public function Randomize(AData:Vector.<Point>, ASkip:int = 1):void
		{
			Generate();
			var i:int;
			var NewIndex:int = 0;
			var vData:Vector.<Point> = new Vector.<Point>;			
			for(i = 0; i<FCellsCount; i++)
			{
				if(i % ASkip == 0)
				{
					NewIndex = Math.round(AData.length / FCellsCount * i);
					vData.push(AData[NewIndex]);
				}
				else
				{					
					vData.push(new Point(0,0));					
				}
			}
			
			trace('Samples: ' + vData.length);			
			trace('Cells: ' + FCellsCount);
			
			var vLeft:Number;
			var vRight:Number;
			var vLeftMediane:Number = new Number();
			var vRightMediane:Number = new Number();			
			var vTrack:int;
			var vLeftInZero:Boolean = true;
			var vPreviousLeft:int = -1;
			var vPreviousRight:int = -1;
			
			for (i = 0; i<FCellsCount; i++)
			{
				vLeftMediane += vData[i].x;
				vRightMediane += vData[i].y;
			}
			vLeftMediane /= vData.length;
			vRightMediane /= vData.length;
			
			for (i = 0; i<FCellsCount; i++)
			{
				vLeft = vData[i].x;
				vRight = vData[i].y;
				
				if(vLeft >= vLeftMediane)
				{
					vTrack = 1 + Math.round(Math.random() * 1);										
					if(vTrack == vPreviousLeft)
					{
						if(vLeftInZero)
						{
							vTrack = 0;
							vLeftInZero = false;
						}
						else						
							vTrack == 1 ? vTrack = 2 : vTrack = 1;						
					}
					AddShapeAtY(vTrack, Math.min(Math.abs(FHeight - FMarkHeight * i), FHeight));
					vPreviousLeft = vTrack;
				}
				
				if(vRight >= vRightMediane)
				{
					vTrack = 3 + Math.round(Math.random() * 1);					
					if(vTrack == vPreviousRight)
					{
						if(!vLeftInZero && vPreviousLeft != 0)
						{
							vTrack = 0;
							vLeftInZero = true;
						}
						else						
							vTrack == 3 ? vTrack = 4 : vTrack = 3;			
					}
					AddShapeAtY(vTrack, Math.min(Math.abs(FHeight - FMarkHeight * i), FHeight));
					vPreviousRight = vTrack;
				}
			}			
		}
		
		private function OnTrackClick(e:MouseEvent):void
		{
			if(e.localY < (height - TRESHHOLD) && e.localY > TRESHHOLD)
				AddShapeAtY(e.target.ID, e.localY);
		}
		
		private function AddShapeAtY(ATrack:int, Y:Number):Sprite
		{
			var Result:Sprite = null;
			if(Y < FHeight && Y > TRESHHOLD)
			{
				Result = new Sprite();
				Result.graphics.beginFill(0xFF9933, 0.5);
				Result.graphics.drawRect(0, 0, TRACK_WIDTH, FMarkHeight);
				Result.graphics.beginFill(0xFF9933, 1);
				Result.graphics.drawRect(0, FMarkHeight - MARK_HEIGHT_ACTUAL, TRACK_WIDTH, MARK_HEIGHT_ACTUAL);
				Result.graphics.endFill();
				Result.graphics.endFill();			
				Result.x = TRACK_WIDTH * ATrack;
				Result.y = Math.floor(Y / FMarkHeight) * FMarkHeight;
				Result.addEventListener(MouseEvent.MOUSE_DOWN, OnMarkClick);
				Result.addEventListener(MouseEvent.MOUSE_OVER, OnMarkHover);
				addChild(Result);
				FMarks.push(Result);
			}
			return Result;
		}
		
		private function OnMarkClick(e:MouseEvent):void
		{
			var vSprite:Sprite = e.target as Sprite;
			removeChild(vSprite);
			for(var i in FMarks)
			{
				if(FMarks[i] == vSprite)
					FMarks.splice(i,1);
			}
		}
		
		private function OnMarkHover(e:MouseEvent):void
		{
			Editor(root).UpdateTrackInfo(e.target as Sprite);
		}
		
		public function XMLStructure(AFileName:String, ATrackName:String):XML
		{
			var vXML:XML = <SoundData></SoundData>;
			vXML.appendChild(<Track File={AFileName} Name={ATrackName} Height={height - TRESHHOLD} Block={MarkHeight}/>);
			vXML.appendChild(<Data></Data>);
			var vIndex:String; 
			var vY:String;
			for(var i in FMarks)
			{
				if(FMarks[i].y < FHeight && FMarks[i].y > TRESHHOLD)
				{
					vIndex = String(FMarks[i].x / 26);
					vY = String(FMarks[i].y);			
					vXML.Data.appendChild(<Mark Index={vIndex} Y={vY}/>);
				}
			}
				
			return vXML;			
		}
		
		public function LoadFromXML(AXML:XML):void
		{			
			var vHeight:Number = new Number(AXML.Track.@Height);
			FMarkHeight = AXML.Track.@Block;
			FRatio = vHeight / FTime;			
			Generate();
			
			var vList:XMLList = AXML.descendants('Mark');
			for(var i in vList)
				AddShapeAtY(vList[i].attribute('Index'), vList[i].attribute('Y'));			
		}
		
		public function set MarkHeight(AValue:Number):void
		{
			FMarkHeight = AValue;
		}
		
		public function get MarkHeight():Number
		{
			return FMarkHeight;
		}
	}	
}
 